/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package BayesNetwork;

import java.util.ArrayList;

/**
 *
 * @author Jedrek
 */
public class ProbabilityNode {
    
    boolean finalNode = false;

    float trueProbability = 0.5f;
    float numberOfOccurences = 0;
    float numberOfTrueOccurences = 0;

    ArrayList<Integer> memory;

    ProbabilityNode trueChild = null;
    ProbabilityNode falseChild = null;

    BayesNode node;

    /**
     * Basic constructor
     * @param node node of a Bayes Network which this object will describe
     * @param trueProbability 
     */
    public ProbabilityNode(BayesNode node, float trueProbability){
        this.node = node;
        finalNode = true;
        this.trueProbability = trueProbability;
        memory = new ArrayList<Integer>();
    }

    public ProbabilityNode(BayesNode node, ProbabilityNode trueChild, ProbabilityNode falseChild){
        this.node = node;
        this.trueChild = trueChild;
        this.falseChild = falseChild;
        memory = new ArrayList<Integer>();
    }

    /**
     * creates a copy of this object and all its children
     * @return a copy
     */
    public ProbabilityNode copy(){
        if(finalNode)
            return new ProbabilityNode(node, trueProbability);
        else
            return new ProbabilityNode(node, trueChild.copy(), falseChild.copy());
    }

    /**
     * computes probability
     * @param event
     * @return probability
     */
    public float getTrueProbability(ArrayList<String> event){
        if(finalNode)
            return trueProbability;

        String name = null;
        char val = 0;

        for(int i = 0; i<event.size(); i++){
            String tmpName = event.get(i).substring(0, event.get(i).length()-2);
            if( node.getName().compareTo(tmpName) == 0 ){
                name = tmpName;
                val = event.get(i).charAt(event.get(i).length()-1);
            }
        }

        if(name == null)
            return 0;

        if(val == 'T')
            return trueChild.getTrueProbability(event);
        if(val == 'F')
            return falseChild.getTrueProbability(event);
        
        return 0;
    }

    public void setTrueProbability(ArrayList<String> event, float value){
        if(finalNode){
            trueProbability = value;
            return;
        }

        String name = null;
        char val = 0;

        for(int i = 0; i<event.size(); i++){
            if( event.get(i).length()<3 )
                continue;
            String tmpName = event.get(i).substring(0, event.get(i).length()-2);
            if( node.getName().compareTo(tmpName) == 0 ){
                name = tmpName;
                val = event.get(i).charAt(event.get(i).length()-1);
                val = Character.toUpperCase(val);
            }
        }

        if(name == null)
            return;

        if(val == 'T'){
            trueChild.setTrueProbability(event, value);
            return;
        }
        if(val == 'F'){
            falseChild.setTrueProbability(event, value);
            return;
        }
    }
    
    /**
     * Adds given event to a lerning set and recomputes probability values
     * @param event new event
     * @param weight how specific this event is (give "1" here)
     * @return true if the new event was accepted
     */
    public boolean teachNode(ArrayList<String> event, float weight){
        
        boolean ret = false;
        boolean found = false;
        char val = 0;
        
        for(int i = 0; i<event.size(); i++){
            String tmpName = event.get(i).substring(0, event.get(i).length()-2);
            if( node.getName().compareTo(tmpName) == 0 ){
                found = true;
                val = event.get(i).charAt(event.get(i).length()-1);
                val = Character.toUpperCase(val);
            }
        }
        
        if( found ){
            if( val == 'T' ){
                if( finalNode ){
                    numberOfOccurences+=weight;
                    numberOfTrueOccurences+=weight;
                    memory.add(0, new Integer(1));
                    if(memory.size()>node.memorySize)
                        memory.remove(memory.size()-1);
//                    System.out.println(node.getName() + " = T");
                    ret = true;
                } else{
                    ret = trueChild.teachNode(event, weight);
                }
            } else if( val == 'F' ){
                if( finalNode ){
                    numberOfOccurences+=weight;
                    memory.add(0, new Integer(0));
                    if(memory.size()>node.memorySize)
                        memory.remove(memory.size()-1);
//                    System.out.println(node.getName() + " = F");
                    ret = true;
                } else{
                    ret = falseChild.teachNode(event, weight);
                }
            } else{
                ret = false;
            }
        } else{
            if( finalNode ){
                //numberOfOccurences++;
                //numberOfTrueOccurences+=0.5f;
                ret = true;
            } else{
                falseChild.teachNode(event, 0.5f*weight);
                ret = trueChild.teachNode(event, 0.5f*weight);
            }
        }
        
        if(ret)
            computProbability();
        
        return ret;
    }
    
    /**
     * recomputes probability for this Pribability Node (it uses Laplacian Smoothing)
     */
    void computProbability(){
//        trueProbability = (numberOfTrueOccurences + BayesNetwork.smoothingCoefficient)/(numberOfOccurences + 2*BayesNetwork.smoothingCoefficient);
        trueProbability = (countTrueOccurences() + BayesNetwork.smoothingCoefficient)/(memory.size() + 2*BayesNetwork.smoothingCoefficient);
    }

    private int countTrueOccurences(){
        int count = 0;
        for(int i = 0; i<memory.size(); i++)
            if(memory.get(i).intValue()==1)
                count++;

        return count;
    }
}
